<?php

namespace App\Http\Controllers;

use App\Exceptions\InvalidRequestException;
use App\Models\Category;
use App\Models\OrderItem;
use App\Models\Product;
use App\SearchBuilders\ProductSearchBuilder;
use App\Services\CategoryService;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;

class ProductsController extends Controller
{
    //原始搜索
    //public function index(Request $request, CategoryService $categoryService)
    //{
    //    //创建一个查询构造器
    //    $builder = Product::where('on_sale',true);
    //    //判断是否有提交search参数，如果有就复制给￥searcha变量
    //    if($search = $request->input('search','')){
    //        $like = '%'.$search.'%';
    //        //模糊搜索商品标题、商品详情、SKU标题、SKU描述
    //        $builder->where(function ($query) use($like){
    //            $query->where('title','like',$like)
    //                ->orwhere('description','like',$like)
    //                ->orwhereHas('skus',function ($query)use($like){
    //                    $query->where('title','like',$like)
    //                        ->orwhere('description','like',$like);
    //                });
    //        });
    //    }
    //
    //    //如果有传入category_id字段，并且在数据库中有对应的类目
    //    if($request->input('category_id') && $category = Category::find($request->input('category_id'))) {
    //        //如果这是一个父类目
    //        if($category->is_directory) {
    //            //则筛选出父类目下所有子类目的商品
    //            $builder->whereHas('category',function ($query) use ($category) {
    //                $query->where('path','like',$category->path.$category->id.'-%');
    //            });
    //        }else{
    //            //如果这不是一个父类目，则直接筛选出此类目下的商品
    //            $builder->where('category_id',$category->id);
    //        }
    //    }
    //
    //    // 是否有提交 order 参数，如果有就赋值给 $order 变量
    //    // order 参数用来控制商品的排序规则
    //    if ($order = $request->input('order', '')) {
    //        // 是否是以 _asc 或者 _desc 结尾
    //        if (preg_match('/^(.+)_(asc|desc)$/', $order, $m)) {
    //            // 如果字符串的开头是这 3 个字符串之一，说明是一个合法的排序值
    //            if (in_array($m[1], ['price', 'sold_count', 'rating'])) {
    //                // 根据传入的排序值来构造排序参数
    //                $builder->orderBy($m[1], $m[2]);
    //            }
    //        }
    //    }
    //
    //    $products = $builder->paginate(16);
    //
    //    return view('products.index',[
    //        'products'=>$products,
    //        //等价于isset($category) ? $category : null
    //        'category' => $category ?? null,
    //        'filters'=>[
    //            'search'=>$search,
    //            'order'=>$order,
    //        ],
    //        //将类目树传递给模板文件
    //        'categoryTree' => $categoryService->getCategoryTree(),
    //        'properties'=>[]
    //    ]);
    //}

    //elasticsearch 搜索
    //public function index(Request $request)
    //{
    //    $page    = $request->input('page', 1);
    //    $perPage = 16;
    //
    //    // 构建查询
    //    $params = [
    //        'index' => 'products',
    //        'body'  => [
    //            'from'  => ($page - 1) * $perPage, // 通过当前页数与每页数量计算偏移值
    //            'size'  => $perPage,
    //            'query' => [
    //                'bool' => [
    //                    'filter' => [
    //                        ['term' => ['on_sale' => true]],
    //                    ],
    //                ],
    //            ],
    //        ],
    //    ];
    //
    //    // 是否有提交 order 参数，如果有就赋值给 $order 变量
    //    // order 参数用来控制商品的排序规则
    //    if ($order = $request->input('order', '')) {
    //        // 是否是以 _asc 或者 _desc 结尾
    //        if (preg_match('/^(.+)_(asc|desc)$/', $order, $m)) {
    //            // 如果字符串的开头是这 3 个字符串之一，说明是一个合法的排序值
    //            if (in_array($m[1], ['price', 'sold_count', 'rating'])) {
    //                // 根据传入的排序值来构造排序参数
    //                $params['body']['sort'] = [[$m[1] => $m[2]]];
    //            }
    //        }
    //    }
    //
    //    if($request->input('category_id') && $category = Category::find($request->input('category_id'))) {
    //        if($category->is_directory) {
    //            //如果是一个父类目，则使用category_path 来筛选
    //            $params['body']['query']['bool']['filter'][] = [
    //                'prefix' => ['category_path' => $category->path.$category->id.'-']
    //            ];
    //        }else{
    //            //否则直接通过category_id筛选
    //            $params['body']['query']['bool']['filter'][] = [
    //                'term' => ['category_id' => $category->id]
    //            ];
    //        }
    //    }
    //
    //    //关键词搜索
    //    if ($search = $request->input('search', '')) {
    //        //将搜索词根据空格拆分cheng数组，并过来掉空项
    //        $keywords = array_filter(explode(' ',$search));
    //        $params['body']['query']['bool']['must'] = [];
    //        foreach ($keywords as $keyword){
    //            $params['body']['query']['bool']['must'][] = [
    //                'multi_match' => [
    //                    'query'  => $keyword,
    //                    'fields' => [
    //                        'title^2',
    //                        'long_title^2',
    //                        'category^2',
    //                        'description',
    //                        'skus_title',
    //                        'skus_description',
    //                        'properties_value',
    //                    ],
    //                ],
    //            ];
    //        }
    //    }
    //
    //    //只有当用户有输入搜索词或者使用了类目筛选的时候才会做集合
    //    if ($search || isset($category)) {
    //        $params['body']['aggs'] = [
    //            'properties' => [
    //                'nested' => [
    //                    'path' => 'properties',
    //                ],
    //                'aggs'   => [
    //                    'properties' => [
    //                        'terms' => [
    //                            'field' => 'properties.name',
    //                        ],
    //                        'aggs'  => [
    //                            'value' => [
    //                                'terms' => [
    //                                    'field' => 'properties.value',
    //                                ],
    //                            ],
    //                        ],
    //                    ],
    //                ],
    //            ],
    //        ];
    //    }
    //
    //
    //    //从用户请求参数获取filters
    //    $propertyFilters = [];
    //    if($filterString = $request->input('filters')) {
    //        //将获取到的字符串用符号|拆分成数组
    //        $filterArray = explode('|',$filterString);
    //        foreach ($filterArray as $filter){
    //            //将字符串用符号：拆分两部分并且分别赋值给name和value两个变量
    //            list($name,$value) = explode(':',$filter);
    //            //去掉已查询的分面搜索
    //            $propertyFilters[$name] = $value;
    //
    //            //添加到filter类型中
    //            $params['body']['query']['bool']['filter'][] = [
    //                //由于我们需要筛选的是nested类型下的属性，因此需要nested查询
    //                'nested' => [
    //                    //指明nested字段
    //                    'path' => 'properties',
    //                    'query' => [
    //                        ['term' => ['properties.search_value' => $filter]],
    //                    ]
    //                ]
    //            ];
    //        }
    //    }
    //
    //
    //
    //    $result = app('es')->search($params);
    //
    //
    //    // 通过 collect 函数将返回结果转为集合，并通过集合的 pluck 方法取到返回的商品 ID 数组
    //    $productIds = collect($result['hits']['hits'])->pluck('_id')->all();
    //    // 通过 whereIn 方法从数据库中读取商品数据
    //    $products = Product::query()
    //        ->whereIn('id', $productIds)
    //        //orderByRaw可以让我们用原生的sqL来给查询结果排序
    //        ->orderByRaw(sprintf("FIND_IN_SET(id, '%s')", join(',', $productIds)))
    //        ->get();
    //    // 返回一个 LengthAwarePaginator 对象
    //    $pager = new LengthAwarePaginator($products, $result['hits']['total']['value'], $perPage, $page, [
    //        'path' => route('products.index', false), // 手动构建分页的 url
    //    ]);
    //
    //    $properties = [];
    //    // 如果返回结果里有 aggregations 字段，说明做了分面搜索
    //    if (isset($result['aggregations'])) {
    //        // 使用 collect 函数将返回值转为集合
    //        $properties = collect($result['aggregations']['properties']['properties']['buckets'])
    //            ->map(function ($bucket) {
    //                // 通过 map 方法取出我们需要的字段
    //                return [
    //                    'key'    => $bucket['key'],
    //                    'values' => collect($bucket['value']['buckets'])->pluck('key')->all(),
    //                ];
    //            })->filter(function ($property) use ($propertyFilters){
    //                //过滤掉只剩下一个值或已经在筛选条件里的属性
    //                return count($property['values']) > 1 && !isset($propertyFilters[$property['key']]) ;
    //            });
    //    }
    //
    //    return view('products.index', [
    //        'products' => $pager,
    //        'filters'  => [
    //            'search' => $search,
    //            'order'  => $order,
    //        ],
    //        'category' => $category ?? null,
    //        'properties' => $properties,
    //        'propertyFilters' => $propertyFilters
    //    ]);
    //}

    //封装后的搜索
    public function index(Request $request)
    {
        $page    = $request->input('page', 1);
        $perPage = 16;
        // 新建查询构造器对象，设置只搜索上架商品，设置分页
        $builder = (new ProductSearchBuilder())->onSale()->paginate($perPage, $page);

        if ($request->input('category_id') && $category = Category::find($request->input('category_id'))) {
            // 调用查询构造器的类目筛选
            $builder->category($category);
        }

        if ($search = $request->input('search', '')) {
            $keywords = array_filter(explode(' ', $search));
            // 调用查询构造器的关键词筛选
            $builder->keywords($keywords);
        }

        if ($search || isset($category)) {
            // 调用查询构造器的分面搜索
            $builder->aggregateProperties();
        }

        $propertyFilters = [];
        if ($filterString = $request->input('filters')) {
            $filterArray = explode('|', $filterString);
            foreach ($filterArray as $filter) {
                list($name, $value) = explode(':', $filter);
                $propertyFilters[$name] = $value;
                // 调用查询构造器的属性筛选
                $builder->propertyFilter($name, $value);
            }
        }

        if ($order = $request->input('order', '')) {
            if (preg_match('/^(.+)_(asc|desc)$/', $order, $m)) {
                if (in_array($m[1], ['price', 'sold_count', 'rating'])) {
                    // 调用查询构造器的排序
                    $builder->orderBy($m[1], $m[2]);
                }
            }
        }

        // 最后通过 getParams() 方法取回构造好的查询参数
        $result = app('es')->search($builder->getParams());
        //封装搜索参数结束


        // 通过 collect 函数将返回结果转为集合，并通过集合的 pluck 方法取到返回的商品 ID 数组
        $productIds = collect($result['hits']['hits'])->pluck('_id')->all();
        // 通过 whereIn 方法从数据库中读取商品数据
        $products = Product::query()
            ->whereIn('id', $productIds)
            //orderByRaw可以让我们用原生的sqL来给查询结果排序
            ->orderByRaw(sprintf("FIND_IN_SET(id, '%s')", join(',', $productIds)))
            ->get();
        // 返回一个 LengthAwarePaginator 对象
        $pager = new LengthAwarePaginator($products, $result['hits']['total']['value'], $perPage, $page, [
            'path' => route('products.index', false), // 手动构建分页的 url
        ]);

        $properties = [];
        // 如果返回结果里有 aggregations 字段，说明做了分面搜索
        if (isset($result['aggregations'])) {
            // 使用 collect 函数将返回值转为集合
            $properties = collect($result['aggregations']['properties']['properties']['buckets'])
                ->map(function ($bucket) {
                    // 通过 map 方法取出我们需要的字段
                    return [
                        'key'    => $bucket['key'],
                        'values' => collect($bucket['value']['buckets'])->pluck('key')->all(),
                    ];
                })->filter(function ($property) use ($propertyFilters){
                    //过滤掉只剩下一个值或已经在筛选条件里的属性
                    return count($property['values']) > 1 && !isset($propertyFilters[$property['key']]) ;
                });
        }

        return view('products.index', [
            'products' => $pager,
            'filters'  => [
                'search' => $search,
                'order'  => $order,
            ],
            'category' => $category ?? null,
            'properties' => $properties,
            'propertyFilters' => $propertyFilters
        ]);
    }

    public function show(Product $product, Request $request)
    {
        //判断商品是否上架，如果没有上架则抛出异常
        if(! $product->on_sale){
            throw new InvalidRequestException('商品未上架');
        }

        $favored = false;
        //用户未登录是返回null，已登录返回对应的用户对象
        if($user = $request->user()){
            //从当前用户已收藏的商品搜索id为当前商品id的商品
            //boolval()函数用户把值转为布尔值
            $favored = boolval($user->favoriteProducts()->find($product->id));
        }
        $reviews = OrderItem::query()
            ->with(['order.user','productSku'])    //预先加载关联关系
            ->where('product_id',$product->id)
            ->whereNotNull('reviewed_at')   //筛选出已评价的
            ->orderBy('reviewed_at','desc') //按评价时间倒序
            ->limit(10)
            ->get();

        //相关推荐
        //创建一个查询构造器，只搜索上架的商品，去搜索结果的前4个商品
        $builder = (new ProductSearchBuilder())->onSale()->paginate(4,1);
        //遍历当前商品的属性
        foreach ($product->properties as $property){
            $builder->propertyFilter($property->name, $property->value, 'should');
        }
        //设置最少匹配一般属性
        $builder->minShouldMatch(ceil(count($product->properties)/2));
        $params = $builder->getParams();
        //同时将当前商品的id排除
        $params['body']['query']['bool']['must_not'] = [['term' => ['_id' => $product->id]]];
        //搜索
        $result = app('es')->search($params);
        $similarProductIds = collect($result['hits']['hits'])->pluck('_id')->all();
        //根据ElasticSearch搜索出来是商品id从数据库获取商品
        $similarProducts = Product::query()
            ->whereIn('id',$similarProductIds)
            ->orderByRaw(sprintf("FIND_IN_SET(id, '%s')",join(',',$similarProductIds)))
            ->get();

        return view('products.show',['product'=>$product,'favored'=>$favored,'reviews'=>$reviews,'similar'=>$similarProducts]);
    }

    public function favor(Product $product, Request $request)
    {
        $user = $request->user();
        if($user->favoriteProducts()->find($product->id)){
            return [];
        }

        $user->favoriteProducts()->attach($product);

        return [];
    }

    public function disfavor(Product $product, Request $request)
    {
        $user = $request->user();
        $user->favoriteProducts()->detach($product);

        return [];
    }

    public function favorites(Request $request)
    {
        $sql = $request->user()->favoriteProducts();
        $total = $sql->count();
        $products = $sql->paginate(2);

        return view('products.favorites',['products'=>$products,'total'=>$total]);
    }
}
